#define PLUGIN_REVISION_R (25)
/*
* Includes
*/
#include "includes.inc"

class anticheatplugin : public sampgdk::EventHandler 
{
public:
    anticheatplugin();
    virtual ~anticheatplugin();
	//virtual void OnGameModeInit();
    //virtual bool OnPlayerStateChange(int playerid, int newstate, int oldstate);
	virtual bool OnPlayerConnect(int playerid);
    virtual bool OnPlayerDisconnect(int playerid, int reason);
	virtual bool OnPlayerSpawn(int playerid);
};

static anticheatplugin theGameMode;
using namespace std;  
using namespace sampgdk;

anticheatplugin::anticheatplugin()
{
    this->Register();
}

anticheatplugin::~anticheatplugin()
{

}
/*
void anticheatplugin::OnGameModeInit()
{
	
}

bool anticheatplugin::OnPlayerStateChange(int playerid, int newstate, int oldstate)
{
	
	return true;
}
*/

#define PI (314159265/100000000)

#define MAX_PLAYERS (500)
#define WEAPONS (55)

//
#define AC_ONMONEY_REPORT   (0)
#define AC_ONMONEY_RESET    (1)

#define AC_DETECTED_DISABLED_WEAPON (0)
#define AC_DETECTED_JETPACK 		(1)
#define AC_DETECTED_MONEY   		(2)
#define AC_DETECTED_SPAWNED_WEAPON  (3)
#define AC_DETECTED_SPEEDHACK		(4)

// Weapons
#define WEAPON_HANDS					(0)
#define WEAPON_BRASSKNUCKLE				(1)
#define WEAPON_GOLFCLUB					(2)
#define WEAPON_NITESTICK				(3)
#define WEAPON_KNIFE					(4)
#define WEAPON_BAT						(5)
#define WEAPON_SHOVEL					(6)
#define WEAPON_POOLSTICK				(7)
#define WEAPON_KATANA					(8)
#define WEAPON_CHAINSAW					(9)
#define WEAPON_DILDO					(10)
#define WEAPON_DILDO2					(11)
#define WEAPON_VIBRATOR					(12)
#define WEAPON_VIBRATOR2				(13)
#define WEAPON_FLOWER					(14)
#define WEAPON_CANE						(15)
#define WEAPON_GRENADE					(16)
#define WEAPON_TEARGAS					(17)
#define WEAPON_MOLTOV					(18)
#define WEAPON_COLT45					(22)
#define WEAPON_SILENCED					(23)
#define WEAPON_DEAGLE					(24)
#define WEAPON_SHOTGUN					(25)
#define WEAPON_SAWEDOFF					(26)
#define WEAPON_SHOTGSPA					(27)
#define WEAPON_UZI						(28)
#define WEAPON_MP5						(29)
#define WEAPON_AK47						(30)
#define WEAPON_M4						(31)
#define WEAPON_TEC9						(32)
#define WEAPON_RIFLE					(33)
#define WEAPON_SNIPER					(34)
#define WEAPON_ROCKETLAUNCHER			(35)
#define WEAPON_HEATSEEKER				(36)
#define WEAPON_FLAMETHROWER				(37)
#define WEAPON_MINIGUN					(38)
#define WEAPON_SATCHEL					(39)
#define WEAPON_BOMB						(40)
#define WEAPON_SPRAYCAN					(41)
#define WEAPON_FIREEXTINGUISHER			(42)
#define WEAPON_CAMERA					(43)
#define WEAPON_PARACHUTE				(46)
#define WEAPON_VEHICLE					(49)
#define WEAPON_DROWN					(53)
#define WEAPON_COLLISION				(54)

#define PLUGIN_PRIVATE_UPDATE_AC (45)

int IsWeaponAllowed[MAX_PLAYERS][WEAPONS];
int CheckWeapon = 1;
int IsJetpackAllowed[MAX_PLAYERS];
int CheckJetpack = 1;
int CheckMoney = 1;
int ActionMoneyCheat = 0;
int PlayerMoney[MAX_PLAYERS];
int HasWeapon[MAX_PLAYERS][WEAPONS];
int SpawnWeaponCheck = 1;
int SpeedCheck = 1;
int g_Ticked = 0;
int g_TickMax = PLUGIN_PRIVATE_UPDATE_AC;
int Classes[500][3];
int ClassWp[500][3];

vector<int> PlayerLoopList;

vector <AMX *> amx_list;

extern void * pAMXFunctions;

int AC_OnCheatDetected(int playerid,int type, int extraid)
{
    int amxerr = (-1),idx;
	for (std::vector<AMX *>::iterator a = amx_list.begin(); a != amx_list.end(); ++a)
	{
		amxerr = amx_FindPublic(* a, "AC_OnCheatDetected", &idx);
		if (amxerr == AMX_ERR_NONE)
		{
			amx_Push(* a, extraid);
			amx_Push(* a, type);
			amx_Push(* a, playerid);
			amx_Exec(* a, NULL, idx);
			return 1;
		}
	}
    return 0;
}

//forward AC_OnCheatDetected(playerid,cheat_type,ac_extra);
//native ACget_UpdateDelay();
static cell AMX_NATIVE_CALL GetAntiCheatUpdateDelay( AMX* amx, cell* params )
{
	return g_TickMax;
}
//native ACget_JetpackAllowed(playerid = 0);
static cell AMX_NATIVE_CALL get_JetpackAllowed( AMX* amx, cell* params )
{
	return IsJetpackAllowed[params[1]];
}
//native ACset_UpdateDelay(ac_ticks);
static cell AMX_NATIVE_CALL SetAntiCheatUpdateDelay( AMX* amx, cell* params )
{
	int gg_divider = params[1];
	if(params[1] >= 1000)
	{
		gg_divider = 1000;
	}else
	if(params[1] <= 5)
	{
		gg_divider = 5;
	}else
	g_TickMax = gg_divider;
	return 1;
}
//native ACset_AllowedWeapon(weaponid,ac_status = 1,playerid = (-1));
static cell AMX_NATIVE_CALL SetAntiCheatAllowedWeapon( AMX* amx, cell* params )
{
	if(params[3] == (-1))
	{
		int i;
		for(i = 0; i < MAX_PLAYERS; i++)
		{
			IsWeaponAllowed[i][params[1]] = params[2];
		}
	}
	else
	{
		IsWeaponAllowed[params[3]][params[1]] = params[2];
	}
	return 1;
}
//native ACget_AllowedWeapon(weaponid,playerid = 0);
static cell AMX_NATIVE_CALL GetAntiCheatAllowedWeapon( AMX* amx, cell* params )
{
	return IsWeaponAllowed[params[2]][params[1]];
}
//native ACset_JetpackAllowed(ac_status = 1,playerid = (-1));
static cell AMX_NATIVE_CALL set_JetpackAllowed( AMX* amx, cell* params )
{
	if(params[2] == (-1))
	{
		int i;
		for(i = 0; i < MAX_PLAYERS; i++)
		{
			IsJetpackAllowed[i] = params[1];
		}
	}
	else
	{
		IsJetpackAllowed[params[2]] = params[1];
	}
	return 1;
}

//native ACset_SpeedCheck(ac_enable = 1);
static cell AMX_NATIVE_CALL set_SpeedCheck( AMX* amx, cell* params )
{
	SpeedCheck = params[1];
	return 1;
}
//native ACget_SpeedCheck();
static cell AMX_NATIVE_CALL get_SpeedCheck( AMX* amx, cell* params )
{
	return SpeedCheck;
}
//native ACset_WeaponCheck(ac_enable = 1);
static cell AMX_NATIVE_CALL set_WeaponCheck( AMX* amx, cell* params )
{
	CheckWeapon = params[1];
	return 1;
}
//native ACreset_PlayerMoney(playerid);
static cell AMX_NATIVE_CALL reset_PlayerMoney( AMX* amx, cell* params )
{
	ResetPlayerMoney(params[1]);
	return 1;
}
//native ACset_JetpackCheck(ac_enable = 1);
static cell AMX_NATIVE_CALL set_JetpackCheck( AMX* amx, cell* params )
{
	CheckJetpack = params[1];
	return 1;
}
//native ACget_WeaponCheck();
static cell AMX_NATIVE_CALL get_WeaponCheck( AMX* amx, cell* params )
{
	return CheckWeapon;
}
//native ACget_JetpackCheck();
static cell AMX_NATIVE_CALL get_JetpackCheck( AMX* amx, cell* params )
{
	return CheckJetpack;
}
//native ACset_MoneyCheck(ac_enable = 1);
static cell AMX_NATIVE_CALL set_MoneyCheck( AMX* amx, cell* params )
{
	CheckMoney = params[1];
	if(params[1] == 1)EnableStuntBonusForAll(false);
	return 1;
}
//native ACget_MoneyCheck();
static cell AMX_NATIVE_CALL get_MoneyCheck( AMX* amx, cell* params )
{
	return CheckMoney;
}
//native ACget_ActionOnMoneyCheat();
static cell AMX_NATIVE_CALL get_MoneyAction( AMX* amx, cell* params )
{
	return ActionMoneyCheat;
}
//native ACset_ActionOnMoneyCheat(ac_action = 0);
static cell AMX_NATIVE_CALL set_MoneyAction( AMX* amx, cell* params )
{
	ActionMoneyCheat = params[1];
	return 1;
}
//native ACget_PlayerMoney(playerid);
static cell AMX_NATIVE_CALL get_PlayerMoney( AMX* amx, cell* params )
{
	return PlayerMoney[params[1]];
}
//native ACgive_PlayerMoney(playerid,amount);
static cell AMX_NATIVE_CALL give_PlayerMoney( AMX* amx, cell* params )
{
	PlayerMoney[params[1]] += params[2];
	GivePlayerMoney(params[1],params[2]);
	return 1;
}
//native ACset_PlayerMoney(playerid,amount);
static cell AMX_NATIVE_CALL set_PlayerMoney( AMX* amx, cell* params )
{
	PlayerMoney[params[1]] = params[2];
	GivePlayerMoney(params[1],(params[2]-GetPlayerMoney(params[1])));
	return 1;
}
//native ACgive_PlayerWeapon(playerid, weaponid, ammo);
static cell AMX_NATIVE_CALL ACgive_PlayerWeapon( AMX* amx, cell* params )
{
	HasWeapon[params[1]][params[2]] = 1;
	GivePlayerWeapon(params[1],params[2],params[3]);
	return 1;
}
//native ACreset_PlayerWeapons(playerid);
static cell AMX_NATIVE_CALL ACreset_PlayerWeapons( AMX* amx, cell* params )
{
	ResetPlayerWeapons(params[1]);
	int i;
	for(i = 0; i < WEAPONS; i++)HasWeapon[params[1]][i] = 0;
	return 1;
}
//native ACset_SpawnWeaponCheck(enable = 1);
static cell AMX_NATIVE_CALL ACset_SpawnWeaponCheck( AMX* amx, cell* params )
{
	SpawnWeaponCheck = params[1];
	return 1;
}

//native ACget_SpawnWeaponCheck();
static cell AMX_NATIVE_CALL ACget_SpawnWeaponCheck( AMX* amx, cell* params )
{
	return SpawnWeaponCheck;
}
//native ACfix_PlayerMoney();
static cell AMX_NATIVE_CALL fix_PlayerMoney( AMX* amx, cell* params )
{
	return GivePlayerMoney(params[1],(PlayerMoney[params[1]]-GetPlayerMoney(params[1])));
}

//native AC_AddPlayerClass(modelid, Float:spawn_x, Float:spawn_y, Float:spawn_z, Float:z_angle, weapon1, weapon1_ammo, weapon2, weapon2_ammo, weapon3, weapon3_ammo);
static cell AMX_NATIVE_CALL n_AddPlayerClass( AMX* amx, cell* params )
{
	return p_AddPlayerClass(params[1],amx_ctof(params[2]),amx_ctof(params[3]),amx_ctof(params[4]),amx_ctof(params[5]),params[6],params[7],params[8],params[9],params[10],params[11]);
}
//native AC_AddPlayerClassEx(teamid, modelid, Float:spawn_x, Float:spawn_y, Float:spawn_z, Float:z_angle, weapon1, weapon1_ammo, weapon2, weapon2_ammo, weapon3, weapon3_ammo);
static cell AMX_NATIVE_CALL n_AddPlayerClassEx( AMX* amx, cell* params )
{
	return p_AddPlayerClassEx(params[1],params[2],amx_ctof(params[3]),amx_ctof(params[4]),amx_ctof(params[5]),amx_ctof(params[6]),params[7],params[8],params[9],params[10],params[11],params[12]);
}

bool anticheatplugin::OnPlayerConnect(int playerid)
{
	p_fix_Player(playerid);
	PlayerLoopList.push_back(playerid);	
	return true;
}

bool anticheatplugin::OnPlayerDisconnect(int playerid, int reason)
{
	int size = PlayerLoopList.size();
	for (int index=0; index < size; ++index)
	{
		if(PlayerLoopList.at(index) == playerid)
		{
			PlayerLoopList.erase(PlayerLoopList.begin()+index);
			break;
		}
	}
	return true;
}

bool anticheatplugin::OnPlayerSpawn(int playerid)
{
	int skin = GetPlayerSkin(playerid);
	if(ClassWp[skin][0] > 0)
	{
		HasWeapon[playerid][Classes[skin][0]] = 1;
	}
	if(ClassWp[skin][1] > 0)
	{
		HasWeapon[playerid][Classes[skin][1]] = 1;
	}
	if(ClassWp[skin][2] > 0)
	{
		HasWeapon[playerid][Classes[skin][2]] = 1;
	}
	return true;
}

PLUGIN_EXPORT unsigned int PLUGIN_CALL Supports() 
{
	return SUPPORTS_VERSION | SUPPORTS_AMX_NATIVES | SUPPORTS_PROCESS_TICK;
}

PLUGIN_EXPORT bool PLUGIN_CALL Load( void **ppData ) 
{
	pAMXFunctions = ppData[PLUGIN_DATA_AMX_EXPORTS];
	Wrapper::GetInstance()->Initialize(ppData);
	for(int i = 0; i < 500; ++i)
	{
			for(int a = 0; a < 55; ++a)
			{
				IsWeaponAllowed[i][a] = 1;
			}
	}
	cout << "    Anti Cheat Plugin 2011 by Gamer_Z loaded\r\n";
	return true;
}

PLUGIN_EXPORT void PLUGIN_CALL Unload( )
{
	cout << "    Anti Cheat Plugin 2011 by Gamer_Z unloaded\r\n";
}

AMX_NATIVE_INFO AMXNatives[ ] =
{
	{"ACset_UpdateDelay", SetAntiCheatUpdateDelay},
	{"ACget_UpdateDelay", GetAntiCheatUpdateDelay},

	{"ACset_AllowedWeapon", SetAntiCheatAllowedWeapon},
	{"ACget_AllowedWeapon", GetAntiCheatAllowedWeapon},

	{"ACget_JetpackAllowed", get_JetpackAllowed},
	{"ACset_JetpackAllowed", set_JetpackAllowed},

	{"ACset_WeaponCheck", set_WeaponCheck},
	{"ACset_JetpackCheck", set_JetpackCheck},

	{"ACget_WeaponCheck", get_WeaponCheck},
	{"ACget_JetpackCheck", get_JetpackCheck},

	{"ACset_MoneyCheck", set_MoneyCheck},
	{"ACget_MoneyCheck", get_MoneyCheck},

	{"ACgive_PlayerMoney", give_PlayerMoney},
	{"ACset_PlayerMoney", set_PlayerMoney},
	{"ACget_PlayerMoney", get_PlayerMoney},
	{"ACreset_PlayerMoney", reset_PlayerMoney},
	{"ACset_ActionOnMoneyCheat", set_MoneyAction},
	{"ACget_ActionOnMoneyCheat", get_MoneyAction},

	{"ACgive_PlayerWeapon", ACgive_PlayerWeapon},
	{"ACreset_PlayerWeapons", ACreset_PlayerWeapons},
	{"ACset_SpawnWeaponCheck",ACset_SpawnWeaponCheck},
	{"ACget_SpawnWeaponCheck",ACget_SpawnWeaponCheck},

	{"ACget_SpeedCheck",get_SpeedCheck},
	{"ACset_SpeedCheck",set_SpeedCheck},

	{"ACfix_PlayerMoney",fix_PlayerMoney},

	{"AC_AddPlayerClassEx",n_AddPlayerClassEx},
	{"AC_AddPlayerClass",n_AddPlayerClass},

	{0,                0}
};

PLUGIN_EXPORT int PLUGIN_CALL AmxLoad( AMX *amx ) 
{
	amx_list.push_back(amx);
	return amx_Register( amx, AMXNatives, -1 );
}

PLUGIN_EXPORT int PLUGIN_CALL AmxUnload( AMX *amx ) 
{
	std::vector<AMX *>::iterator i = amx_list.begin(); 
	while (i != amx_list.end())
	{ 
		i = amx_list.erase(i); 
	} 
	return AMX_ERR_NONE;
}

float s[3];
char sz[64];

PLUGIN_EXPORT void PLUGIN_CALL
	ProcessTick()
{
	if(g_Ticked == g_TickMax)
	{
		int playerid = 0;	
		int action = 0;
		int size = PlayerLoopList.size();
		int weaponid = 0, ammo = 0, i = 0;
		for (int index = 0; index < size; ++index)
		{
			playerid = PlayerLoopList.at(index);
			if(SpeedCheck == 1)
			{
				if(GetPlayerState(playerid) == PLAYER_STATE_DRIVER)
				{
					int vid = GetPlayerVehicleID(playerid);
					GetVehicleVelocity(vid,s[0],s[1],s[2]);
					s[0] = pow(s[0],2.0f);
					s[1] = pow(s[1],2.0f);
					s[2] = pow(s[2],2.0f);
					if(sqrt(s[0]+s[1]+s[2])*175.0f > (MaxVehicleSpeed[GetVehicleModel(vid)]+6.75f))
					{
						if(sqrt(s[0]+s[1])*175.0f > 20.0 && s[2] > 0)
						{
							AC_OnCheatDetected(playerid,AC_DETECTED_SPEEDHACK,GetPlayerVehicleID(playerid));
						}
						continue;
					}
				}
			}
			if(CheckWeapon == 1)
			{
				if(SpawnWeaponCheck == 1)
				{
					for(i = 0; i < 13; i++)
					{
						GetPlayerWeaponData(playerid,i,weaponid,ammo);
						if(ammo > 0 && weaponid != 0)
						{
							if(IsWeaponAllowed[playerid][weaponid] == 0)
							{
								if(IsWeaponClassAllowed(playerid,weaponid) == false)
								{
									AC_OnCheatDetected(playerid,AC_DETECTED_DISABLED_WEAPON,weaponid);
								}
								continue;
							}else
							if(HasWeapon[playerid][weaponid] == 0)
							{
								AC_OnCheatDetected(playerid,AC_DETECTED_SPAWNED_WEAPON,weaponid);
								continue;
							}
						}
					}
				}
				else
				{
					for(i = 0; i < 13; i++)
					{
						GetPlayerWeaponData(playerid,i,weaponid,ammo);
						if(ammo > 0 && weaponid != 0)
						{
							if(IsWeaponAllowed[playerid][weaponid] == 0)
							{
								if(IsWeaponClassAllowed(playerid,weaponid) == false)
								{
									AC_OnCheatDetected(playerid,AC_DETECTED_DISABLED_WEAPON,weaponid);
								}
								continue;
							}
						}
					}
				}
			}
			if(CheckJetpack == 1)
			{
				action = GetPlayerSpecialAction(playerid);
				if(action == 2)
				{
					if(IsJetpackAllowed[playerid] == 0)
					{
						AC_OnCheatDetected(playerid,AC_DETECTED_JETPACK,action);
						continue;
					}
				}
			}
			if(CheckMoney == 1)
			{
				action = GetPlayerMoney(playerid);
				if(action > PlayerMoney[playerid])
				{
					if(ActionMoneyCheat == 0)
					{
						AC_OnCheatDetected(playerid,AC_DETECTED_MONEY,(PlayerMoney[playerid]-action));
						continue;
					}else
					{
						GivePlayerMoney(playerid,(PlayerMoney[playerid]-action));
					}
				}
				else if(action < PlayerMoney[playerid])
				{
					PlayerMoney[playerid] = action;
				}
			}
		}
		g_Ticked = -1;
	}
	g_Ticked += 1;
}

int p_AddPlayerClass(int skin, float x, float y, float z, float Angle, int weapon1, int weapon1_ammo, int weapon2, int weapon2_ammo, int weapon3, int weapon3_ammo)
{
	Classes[skin][0] = weapon1;
	ClassWp[skin][0] = weapon1_ammo;
	Classes[skin][1] = weapon2;
	ClassWp[skin][1] = weapon2_ammo;
	Classes[skin][2] = weapon3;
	ClassWp[skin][2] = weapon3_ammo;

	return AddPlayerClass(skin,x,y,z,Angle,weapon1,weapon1_ammo,weapon2,weapon2_ammo,weapon3,weapon3_ammo);
}

int p_AddPlayerClassEx(int team,int skin, float x, float y, float z, float Angle, int weapon1, int weapon1_ammo, int weapon2, int weapon2_ammo, int weapon3, int weapon3_ammo)
{
	Classes[skin][0] = weapon1;
	ClassWp[skin][0] = weapon1_ammo;
	Classes[skin][1] = weapon2;
	ClassWp[skin][1] = weapon2_ammo;
	Classes[skin][2] = weapon3;
	ClassWp[skin][2] = weapon3_ammo;

	return AddPlayerClassEx(team,skin,x,y,z,Angle,weapon1,weapon1_ammo,weapon2,weapon2_ammo,weapon3,weapon3_ammo);
}

bool IsWeaponClassAllowed(int playerid,int weaponid)
{
	int skin = GetPlayerSkin(playerid);
	if(Classes[skin][0] == weaponid)return true;
	if(Classes[skin][1] == weaponid)return true;
	if(Classes[skin][2] == weaponid)return true;
	return false;
}

int p_fix_Player(int playerid)
{
	ResetPlayerWeapons(playerid);
	int i;
	for(i = 0; i < WEAPONS; i++)HasWeapon[playerid][i] = 0;
	GivePlayerMoney(playerid,(PlayerMoney[playerid]-GetPlayerMoney(playerid)));
	return 1;
}
